package org.uma.kri.nyu.mor.almas.util;

import java.text.Normalizer;
import java.text.Normalizer.Form;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.LinkedHashMap;
import java.util.Map;

import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.uma.kri.nyu.mor.almas.application.AlmasApplication;
import org.uma.kri.nyu.mor.almas.entity.Contacts;
import org.uma.kri.nyu.mor.almas.entity.Email;
import org.uma.kri.nyu.mor.almas.entity.Group;
import org.uma.kri.nyu.mor.almas.entity.Phone;

import android.accounts.Account;
import android.accounts.AccountManager;
import android.app.Activity;
import android.content.ContentResolver;
import android.content.Context;
import android.database.Cursor;
import android.provider.ContactsContract;
import android.provider.ContactsContract.CommonDataKinds;
import android.provider.ContactsContract.CommonDataKinds.StructuredName;
import android.provider.ContactsContract.Groups;

/**
 * コンタクトユーティリティクラス
 * @author uma
 */
public class ContactsUtil {
	
	/** アカウントタイプフィルター */
	private static final String[] ACCOUNT_TYPE_FILTER = {"com.google", "com.android.nttdocomo"};
	
	/** Unicodeの最終文字 */
	private static final String LAST_UNICODE_STR = new String(Character.toChars(0xffff));
	
	/** アカウント一覧取得 */
	public static ArrayList<String> getAccountList() {
		
		// コンテキスト
		Context context = AlmasApplication.getInstance().getApplicationContext();
		
		// アカウント名リスト
		ArrayList<String> accountList = new ArrayList<String>();
		// アカウント一覧を取得
		Account[] accounts = AccountManager.get(context).getAccounts();
		// アカウントタイプがフィルターに該当するもののアカウントリストに追加
		for (Account account : accounts) {
			if(ArrayUtils.contains(ACCOUNT_TYPE_FILTER, account.type)){
				accountList.add(account.name);
			}
		}
		
		// ソート
		Collections.sort(accountList);
		
		return accountList;
	}
	
	/**
	 * 連絡先一覧取得
	 * @param accountName アカウント名
	 * @return 連絡先一覧
	 */
	public static ArrayList<Contacts> getContactsList(String accountName){
		return getContactsList(AlmasApplication.getCurrentActivity(), accountName, null);
	}
	
	/**
	 * グループ取得
	 * @param accountName アカウント名
	 * @param groupName グループ名
	 * @return 連絡先一覧
	 */
	public static ArrayList<Contacts> getContactsList(String accountName, String groupName){
		return getContactsList(AlmasApplication.getCurrentActivity(), accountName, groupName);
	}
	
	/**
	 * 連絡先一覧取得
	 * @param activity アクティビティ
	 * @param accountName アカウント名
	 * @return 連絡先一覧
	 */
	private static ArrayList<Contacts> getContactsList(Activity activity, String accountName, String groupName){
		
		// 対象アカウントのグループ名マップを取得
		Map<String, String> groupMap = getGroupList(activity, accountName);
		
		// 検索条件リスト
		ArrayList<String> sectionList = new ArrayList<String>();
		// 検索条件パラメータリスト
		ArrayList<String> sectionArgList = new ArrayList<String>();
		
		//TODO:条件作成はまだ不明確の為、要調査
		//----------------
		// 検索条件作成
		//----------------
		// アカウント名
		sectionList.add(ContactsContract.RawContacts.ACCOUNT_NAME + "= ?");
		sectionArgList.add(accountName);
		// 表示対象のみ
		sectionList.add(ContactsContract.Data.IN_VISIBLE_GROUP + "= ?");
		sectionArgList.add("1");
		
		//----------------
		// 表示順作成
		//----------------
		ArrayList<String> sortOrderList = new ArrayList<String>();
		sortOrderList.add(ContactsContract.Data.RAW_CONTACT_ID + " ASC");
		sortOrderList.add(ContactsContract.Data._ID + " ASC");
		
		//TODO:不要項目は取得しないように条件を追加予定
		// 取得項目（SELECT）
		String[] projection = null;
		// 条件（WHERE）
		String selection = StringUtils.join(sectionList, " AND ");
		// 条件パラメータ（WHEREのArgs）
		String[] selectionArgs = sectionArgList.toArray(new String[0]);
		// ソート順（ORDER BY）
		String sortOrder = StringUtils.join(sortOrderList, ", ");
		
		
		ContentResolver contentResolver = activity.getContentResolver();
		
		// 連絡先情報取得
		Cursor contentsCursor = contentResolver.query(ContactsContract.Data.CONTENT_URI, projection ,selection, selectionArgs, sortOrder);
		
		ArrayList<Contacts> contactsList = new ArrayList<Contacts>();
		
		int bfRawContactId = -1;
		Contacts contacts = null;
		while (contentsCursor.moveToNext()) {
			
			// コンタクトID取得
			int rawContactId = contentsCursor.getInt(contentsCursor.getColumnIndex(ContactsContract.Data.RAW_CONTACT_ID));
			
			// コンタクトIDに差異がある場合に、新規連絡先クラスを生成して追加
			if(bfRawContactId != rawContactId){
				contacts =  new Contacts();
				contactsList.add(contacts);
				
				// コンタクトID設定
				contacts.rawContactId = rawContactId;
				bfRawContactId = rawContactId;
			}
			
			// MIME-TYPE取得
			String mimeType = contentsCursor.getString(contentsCursor.getColumnIndex(ContactsContract.Data.MIMETYPE));
			
			//-------------------------------
			// MIME-TYPE毎に処理分岐
			//-------------------------------
			// 名称
			if(StructuredName.CONTENT_ITEM_TYPE.equals(mimeType)){
				contacts.firstName = contentsCursor.getString(contentsCursor.getColumnIndex(ContactsContract.Data.DATA2));
				contacts.familyName = contentsCursor.getString(contentsCursor.getColumnIndex(ContactsContract.Data.DATA3));
				contacts.firstNameAlt = contentsCursor.getString(contentsCursor.getColumnIndex(ContactsContract.Data.DATA7));
				contacts.familyNameAlt = contentsCursor.getString(contentsCursor.getColumnIndex(ContactsContract.Data.DATA9));
				
				contacts.displayName = contentsCursor.getString(contentsCursor.getColumnIndex(ContactsContract.Data.DISPLAY_NAME));
//TODO Xperia GXだと取得できなかったため、一時的にコメントアウトとし要調査
//				contacts.contactLabel = contentsCursor.getString(contentsCursor.getColumnIndex("phonebook_label"));
				String sortKey = contentsCursor.getString(contentsCursor.getColumnIndex("sort_key"));
				
				// フリガナから頭文字を作成
				String contactLabel = Normalizer.normalize(sortKey.substring(0, 1), Form.NFKD);
				contactLabel = zenkakuHiraganaToZenkakuKatakana(contactLabel);
				contacts.contactLabel = contactLabel.substring(0, 1);
				
				// フリガナを正規化後に設定（英字が日本語より末尾にくるように英字の場合は、先頭にユニコードの最終文字を追加）
				contacts.sortKey = Normalizer.normalize(sortKey, Form.NFKC);
				// フリガナをノーマライズ（英字は半角に、半角カナは全角カナに変換）
				contacts.sortKey = zenkakuHiraganaToZenkakuKatakana(contacts.sortKey);
				// 先頭文字を取得し、先頭文字が英字の場合は、末尾に行くように先頭に漢字を追加
				char char1 = contacts.sortKey.charAt(0);
				if ((char1 <= '\u007e') || // 英数字
						(char1 == '\u00a5') || // \記号
						(char1 == '\u203e')  // ~記号
				) {
					contacts.sortKey = LAST_UNICODE_STR + contacts.sortKey;
				}
			}
			// グループ
			else if(CommonDataKinds.GroupMembership.CONTENT_ITEM_TYPE.equals(mimeType)){
				Group group = new Group();
				contacts.groupList.add(group);
				
				group.id = contentsCursor.getInt(contentsCursor.getColumnIndex(ContactsContract.Data._ID));
				group.groupId = contentsCursor.getString(contentsCursor.getColumnIndex("group_sourceid"));
				group.groupName = groupMap.get(group.groupId);
			}
			// 電話番号
			else if(CommonDataKinds.Phone.CONTENT_ITEM_TYPE.equals(mimeType)){
				Phone phoneNo = new Phone();
				contacts.phoneNoList.add(phoneNo);
				
				int phoneNoTypeCd = contentsCursor.getInt(contentsCursor.getColumnIndex(ContactsContract.Data.DATA2));
				
				phoneNo.id = contentsCursor.getInt(contentsCursor.getColumnIndex(ContactsContract.Data._ID));
				phoneNo.phoneNo = contentsCursor.getString(contentsCursor.getColumnIndex(ContactsContract.Data.DATA1));
				
				switch (phoneNoTypeCd) {
				case 0:
					// カスタム
					phoneNo.phoneNoType = contentsCursor.getString(contentsCursor.getColumnIndex(ContactsContract.Data.DATA3));
					break;
				default:
					// カスタム以外の場合
					phoneNo.phoneNoType = getCachedNumberLabel(phoneNoTypeCd);
				}
				
			}
			// メールアドレス
			else if(CommonDataKinds.Email.CONTENT_ITEM_TYPE.equals(mimeType)){
				Email mailAdress = new Email();
				contacts.mailAdressList.add(mailAdress);
				
				int mailAdressTypeCd = contentsCursor.getInt(contentsCursor.getColumnIndex(ContactsContract.Data.DATA2));
				
				mailAdress.id = contentsCursor.getInt(contentsCursor.getColumnIndex(ContactsContract.Data._ID));
				mailAdress.mailAdress = contentsCursor.getString(contentsCursor.getColumnIndex(ContactsContract.Data.DATA1));
				
				switch (mailAdressTypeCd) {
				case 0:
					// カスタム
					mailAdress.mailAdressType = contentsCursor.getString(contentsCursor.getColumnIndex(ContactsContract.Data.DATA3));
					break;
				default:
					// カスタム以外の場合
					mailAdress.mailAdressType = getCachedNumberLabel(mailAdressTypeCd);
				}
			}
		}
		
		// 連絡先をソートキーでソート
		Collections.sort(contactsList, new ContactsComparator());
		
//テスト用ログ出力
//		for(Contacts entity : contactsList){
//			Log.d("【動作確認】", entity.contactLabel + "：" + entity.sortKey);
//		}
		
		return contactsList;
	}
	
	/**
	 * 種別ラベル名を取得
	 * @param cachedNumberType 種別コード
	 * @return ラベル名
	 */
	private static String getCachedNumberLabel(int cachedNumberType){
		
		String label = "";
		
		/*
		 * 自宅：1 / 携帯：2 / 勤務先：3 / FAX(勤務先)：4
		 * FAX(自宅)：5 / ポケベル：6 / その他：7
		 * コールバック：8 / クルマ：9 / 会社代表番号：10
		 * ISDN： 11 / メイン：12 / FAX(その他)：13
		 * 無線：14 / テレックス：15 / TTY TTD：16
		 * 携帯電話(勤務先)：17 / ポケベル(勤務先)：18
		 * アシスタント：19 / MMS：20 / カスタム：0
		 */
		switch (cachedNumberType) {
		case 1:
			// 自宅
			label = "自宅";
			break;
		case 2:
			// 携帯
			label = "携帯";
			break;
		case 3:
			// 勤務先
			label = "勤務先";
			break;
		case 4:
			// FAX(勤務先)
			label = "FAX(勤務先)";
			break;
		case 5:
			// FAX(自宅)
			label = "FAX(自宅)";
			break;
		case 6:
			// ポケベル
			label = "ポケベル";
			break;
		case 8:
			// コールバック
			label = "コールバック";
			break;
		case 9:
			// クルマ
			label = "クルマ";
			break;
		case 10:
			// 会社代表番号
			label = "会社代表番号";
			break;
		case 11:
			// ISDN
			label = "ISDN";
			break;
		case 12:
			// メイン
			label = "メイン";
			break;
		case 13:
			// FAX(その他)
			label = "FAX(その他)";
			break;
		case 14:
			// 無線
			label = "無線";
			break;
		case 15:
			// テレックス
			label = "テレックス";
			break;
		case 16:
			// TTY TTD
			label = "TTY TTD";
			break;
		case 17:
			// 携帯電話(勤務先)
			label = "携帯電話(勤務先)";
			break;
		case 18:
			// ポケベル(勤務先)
			label = "ポケベル(勤務先)";
			break;
		case 19:
			// アシスタント
			label = "アシスタント";
			break;
		case 20:
			// MMS
			label = "MMS";
			break;
		default:
			break;
		}
		
		return label;
	}
	
	/**
	 * Group一覧生成.
	 * グループの一覧を取得するカーソルを生成し返します。
	 * @return グループ一覧を取得するカーソル
	 */
	private static Map<String, String> getGroupList(Activity activity, String accountName) {
		
		Map<String, String> groupMap = new LinkedHashMap<String, String>();;
		
		ContentResolver cr = activity.getContentResolver();
		Cursor cursor = cr.query(Groups.CONTENT_URI,
										new String[] {
												Groups._ID,
												Groups.SOURCE_ID,
												Groups.TITLE},
										null,
										null,
										null);
//		groupMap.put("1", "MyContacts");
		
		while(cursor.moveToNext()) {
//				String groupId = cursor.getString(cursor.getColumnIndex(Groups._ID));
				String groupSourceId = cursor.getString(cursor.getColumnIndex(Groups.SOURCE_ID));
				String groupName = cursor.getString(cursor.getColumnIndex(Groups.TITLE));
				groupMap.put(groupSourceId, groupName);
		}
		
		return groupMap;
	}
	
	/**
	 * 連絡先ソートクラス
	 */
	private static class ContactsComparator implements Comparator<Contacts>{
		public int compare(Contacts a, Contacts b){
			
			return a.sortKey.compareTo(b.sortKey);
		}
	}
	
	/**
	 * 全角ひらがなを全角カタカナに変換
	 * @param s 変換文字列
	 * @return 変換後文字列
	 */
	public static String zenkakuHiraganaToZenkakuKatakana(String s) {
		StringBuffer sb = new StringBuffer(s);
		for (int i = 0; i < sb.length(); i++) {
			char c = sb.charAt(i);
			if (c >= 'ぁ' && c <= 'ん') {
				sb.setCharAt(i, (char) (c - 'ぁ' + 'ァ'));
			}
		}
		return sb.toString();
	}
}
